package edu.washington.cs.workflow.analysis.guistructure;

import java.util.LinkedList;
import java.util.List;
import java.util.Stack;

import org.uispec4j.Panel;
import org.uispec4j.Trigger;
import org.uispec4j.UIComponent;
import org.uispec4j.Window;
import org.uispec4j.interception.WindowInterceptor;

import edu.washington.cs.workflow.analysis.guistructure.uievents.UIEvent;
import edu.washington.cs.workflow.analysis.guistructure.uievents.UIEventFactory;
import edu.washington.cs.workflow.analysis.guistructure.uievents.UIEventOutcome;
import edu.washington.cs.workflow.util.Globals;
import edu.washington.cs.workflow.util.Utils;

public class EventSequenceReplayer {

	public final EventSequence sequence;
	public final Panel mainWindow;
	
	public EventSequenceReplayer(EventSequence seq, Panel mainWindow) {
		Utils.checkNotNull(seq);
		Utils.checkNotNull(mainWindow);
		this.sequence = seq;
		this.mainWindow = mainWindow;
	}
	
	public SequenceExecutionOutcome replay() {
		Stack<List<Panel>> activePanelStack = new Stack<List<Panel>>();
		List<Panel> initPanels = new LinkedList<Panel>();
		Throwable error = null;
		initPanels.add(mainWindow);
		activePanelStack.push(initPanels);
		
		Utils.debugPrintln("Before replay... main WIndow.." + System.identityHashCode(mainWindow));
		Utils.debugPrintln(mainWindow.getDescription());
		Utils.debugPrintln("Length of sequence to replay: " + this.sequence.getEventList().size() + ", result: " + this.sequence.outcome);
		Utils.debugPrintln(Globals.lineSep);
		
		//get the first active panel
		List<Panel> activePanels = activePanelStack.peek();
		for(int index = 0; index < sequence.getEventList().size(); index++) {
			UIEvent uiEvent = sequence.getEventList().get(index);
			Utils.checkTrue(!activePanels.isEmpty());
			
			Utils.debugPrintln("Replaying: " + uiEvent.getDescription() + ", active panels? " + activePanels.size());
			
			//get the recorded panel for the ui event to replay, find the corresponding matched panel
			//on the current active panel list, and then find the corresponding ui on the active panel
			Panel currentPanel = uiEvent.getCurrentPanel();
//			UIComponent uic = uiEvent.getUIComponent();
			Panel matchedPanel = Spec4JUtils.getMatchedPanel(currentPanel, activePanels);
			Utils.checkNotNull(matchedPanel);
			UIComponent matchedUIC = Spec4JUtils.getMatchedComponent(uiEvent, currentPanel, matchedPanel);
			Utils.checkNotNull(matchedUIC);
			
			//construct the same event on the matched panel and fire it
			//this method also set the window flag
			UIEvent replayEvent = UIEventFactory.replicateExecutedEvent(uiEvent, matchedUIC, matchedPanel);
			
			//fire the event, and the outcome must be not null
			UIEventOutcome eventOutcome = replayEvent.fireEvent();
			Utils.checkNotNull(eventOutcome, "The outcome is null, for event: " + replayEvent);
			
			//analysis the execution result
			boolean closeCurrent = eventOutcome.closeCurrentPanel();
			boolean newWindow = eventOutcome.hasNewWindow();
			
			if(closeCurrent) {
				Utils.checkTrue(!newWindow);
				activePanels.remove(matchedPanel);
				if(activePanels.isEmpty()) {
					activePanelStack.pop(); //remove the activepanels from the stack
					if(index != sequence.getEventList().size() - 1) {
						Utils.checkTrue(!activePanelStack.isEmpty());
						activePanels = activePanelStack.peek(); //assign new panels (a modal dialog has been closed)
					} //do nothing, return empty active panels
				} //continue execute is activePanels is not empty
			} else if (newWindow) {
				if(eventOutcome.isModalWindow()) {
					//clear the current active panels, and add the poped up window
					Trigger t = eventOutcome.getTrigger();
					Panel modalDialog = null;
					try {
					    modalDialog = Spec4JUtils.getModalDialog(t); //execute it
					} catch (Throwable e) {
						/**
						 * It is possible there is some non-determinstic errors
						 * */
						return ExceptionalExecution.makeExceptionalExecution(e, activePanels);
					}
					//create new panel list with the poped-up modal dialog
					activePanels = new LinkedList<Panel>();
					activePanels.add(modalDialog);
					activePanelStack.push(activePanels);
				} else if (eventOutcome.isNonModalWindow()) {
					//add a non-modal window
					Trigger t = eventOutcome.getTrigger();
					Window w = null;
					try {
					   w = WindowInterceptor.run(t);
					} catch (Throwable e) {
						/**
						 * It is possible there is some non-deterministic errors
						 * */
						return ExceptionalExecution.makeExceptionalExecution(e, activePanels);
					}
					Utils.checkTrue(w != null);
					activePanels.add(w); //add to the active panel, not change the stack
				} else {
					throw new Error("What on hell the outcome is: " + eventOutcome);
				}
			}
			
			//if there is some error, it must be at the last event
			error = eventOutcome.getError();
			if(error != null) {
				Utils.checkTrue(index == sequence.getEventList().size() - 1); //must be the last one
			}
		}
		
		//the outcome
		if(error == null) {
			return NormalExecution.makeNormalExecution(activePanels);
		} else {
			return ExceptionalExecution.makeExceptionalExecution(error, activePanels);
		}
	}
}
